import Dep from './Dep';
import { isElementNode, isDirective } from './utils';

export default class Compiler {
  constructor(vm) {
    this.vm = vm;
    this.compile(this.vm.$el);
  }

  compile($el) {
    //在vue中此处应该是遍历所有的虚拟节点而不是真实节点
    const childNodes = $el.childNodes;
    [...childNodes].forEach((node) => {
      //元素节点时
      if (isElementNode(node)) {
        //分析元素节点
        this.analyzeElementNode(this.vm, node);
        //递归编译子节点
        this.compile(node);
      } else {
        //文本节点时
        this.compileTextNode(this.vm, node);
      }
    });
  }

  analyzeElementNode(vm, node) {
    const attrs = node.attributes;
    //<h1>标签没有定义属性
    //<span>标签有定义多个属性
    // console.log(attrs);
    /**
     * NamedNodeMap {
     *   0: type,
     *   1: placeholder,
     *   2: v-model,
     *   type: type,
     *   placeholder: placeholder,
     *   v-model: v-model,
     *   length: 3
     * }
     */
    [...attrs].forEach((attr) => {
      // console.log(attr);
      //如 v-model="title" type="text"等

      const attrName = attr.name;
      // console.log(attrName);
      // type placeholder v-model

      if (isDirective(attrName)) {
        const attrVal = attr.value;
        // console.log(attrVal);
        //text Please type some... title
        // console.log(attrName); v-model
        // console.log(node);
        //<input type="text" placeholder="Please type some..." v-model="title">

        const type = attrName.split('-')[1];
        // console.log(type); model
        //分析指令是否是v-model(有可能是v-if, v-show等)，如果是才进行语法糖包装。
        if (type === 'model') {
          this.compileVModel(vm, attrVal, node);
        }
      }
    });
  }

  compileVModel(vm, attrVal, node) {
    //定义一个更新视图的函数给dep对象去收集
    const cb = function () {
      //v-model="数据来源" 的实现 此写法会将开发者编写的title属性的值呈现在视图上
      node.value = vm.$data[attrVal];
    };

    vm.dep.collect(attrVal, cb);
    // console.log(vm.dep);
    //Dep {title: [ƒ]}

    //首次执行
    cb();

    //监听节点input事件，可以实现修改视图上的内容时会同时修改实例data对象里的属性
    node.addEventListener(
      'input',
      (e) => {
        //vm.$data.title = 'This is title123';
        vm.$data[attrVal] = e.target.value;
        // console.log(vm.$data);
        //Proxy {title: 'This is title123', todoData: Array(0)}
      },
      false
    );
    //清除v-model属性避免在渲染视图中显示v-model属性
    node.removeAttribute('v-model');
  }

  compileTextNode(vm, node) {
    const reg = /\{\{(.+?)\}\}/;
    const text = node.textContent;
    // console.log(text); {{ title }}
    //正则表达式与指定的字符串匹配时 即成功匹配
    //如果不是{{xxx}}格式的内容就不去做编译
    if (reg.test(text)) {
      const res = text.replace(reg, (n, k) => {
        // console.log(n, k); {{ title }}  title
        k = k.trim();
        // console.log(vm.$data[k]);

        //更新文本节点视图
        const cb = function () {
          node.textContent = vm.$data[k];
        };
        //收集依赖
        vm.dep.collect(k, cb);

        //替换模版数据，首次视图显示
        return vm.$data[k];
      });
      // console.log(res);
      //This is title
      //将匹配替换的结果即实例对象里的数据作为视图显示的内容
      node.textContent = res;
    }
  }
}
